package at.ac.tuwien.dse.health.security.web;

import at.ac.tuwien.dse.health.security.SecurityContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.Serializable;
import java.util.regex.Pattern;

/**
 * This HttpFilter is used for authentication.
 * <p/>
 * To store the information, if a session is authenticated or not, the <code>SESSION_ATTRIBUTE</code> in the
 * HTTP session is used
 *
 * @author Bernhard Nickel
 * @author Gregor Schauer
 * @author Dominik Strasser
 */
public class SecurityFilter implements Filter {
	private static final Logger logger = LogManager.getLogger(SecurityFilter.class);

	private static final String SESSION_ATTRIBUTE = "__HEALTH_login";
	private static final String SESSION_ROLE_OBJECT = "__HEALTH_role_object";

	private Pattern pattern;
	private static boolean debug;

	public static void setAuthenticated(HttpSession session, Serializable roleObject) {
		if (session != null) {
			session.setAttribute(SESSION_ATTRIBUTE, true);
			session.setAttribute(SESSION_ROLE_OBJECT, roleObject);
			SecurityContext.setRole(roleObject);
			if (debug) {
				logger.debug("Authenticated session " + session.getId() + ". Role object is " + roleObject);
			}
		} else {
			SecurityContext.setRole(null);
		}
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		debug = logger.isDebugEnabled();
		String loginUrl = filterConfig.getInitParameter("LOGIN_URL");
		if (loginUrl == null || loginUrl.trim().isEmpty()) {
			logger.error("No login url (configuration parameter LOGIN_URL) specified.");
			return;
		}
		pattern = Pattern.compile(loginUrl);
		logger.info(filterConfig.getFilterName() + " configured. Using login url " + loginUrl);
	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpSession session = request.getSession(true);
		Object checkSession = session.getAttribute(SESSION_ATTRIBUTE);
		if (checkSession != null && checkSession.equals(true)) {
			if (debug) {
				logger.debug("Session " + session.getId() + " is valid");
			}
			SecurityContext.setRole(session.getAttribute(SESSION_ROLE_OBJECT));
			performFilterChain(servletRequest, servletResponse, filterChain);
		} else if (pattern != null && pattern.matcher(request.getRequestURI()).matches()) {
			if (debug) {
				logger.debug("Accessing login url " + request.getRequestURI());
			}
			performFilterChain(servletRequest, servletResponse, filterChain);
		} else {
			if (debug) {
				logger.debug("Access denied for session " + session.getId() + " on " + request.getRequestURI());
			}
			((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		}
	}

	private void performFilterChain(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		try {
			filterChain.doFilter(servletRequest, servletResponse);
		} finally { //remove the role from this thread again
			SecurityContext.setRole(null);
		}
	}

	@Override
	public void destroy() {

	}
}
